package com.terrier.ui.managers
{
    import com.terrier.ui.core.IWxToolTip;
    import com.terrier.ui.core.IWxToolTipManagerClient;
    import com.terrier.ui.core.WxDefaultToolTip;
    import com.terrier.ui.core.WxSprite;
    import com.terrier.ui.effects.IWxAbstractEffect;
    import com.terrier.ui.effects.WxAlphaEffect;
    import com.terrier.ui.events.WxSystemEvent;
    import com.terrier.ui.events.WxEffectEvent;
    
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.geom.Point;
    import flash.utils.Timer;

    /**
     * 组件提示管理类
     *
     * @author Jarvis.weng
     */
    public class WxToolTipManager extends EventDispatcher
    {
        private static var s_instance:WxToolTipManager;

        public static function get instance():WxToolTipManager
        {
            if (s_instance == null)
            {
                s_instance = new WxToolTipManager(new Singleton());
            }
            return s_instance;
        }

        private var _stageWidth:Number = 100;

        private var _stageHeight:Number = 100;

        private var _enabled:Boolean = true;

        private var _initialized:Boolean;

        private var _showTimer:Timer;

        private var _hideTimer:Timer;

        private var _currentTarget:DisplayObject;

        private var _previousTarget:DisplayObject;

        private var _currentToolTip:IWxToolTip;

        private var _currentTipInfo:Object;

        private var _currentShowEffect:IWxAbstractEffect;

        private var _currentHideEffect:IWxAbstractEffect;

        private var _currentShowDelay:Number;

        private var _currentHideDelay:Number;

        private var _defaultToolTipClass:Class = WxDefaultToolTip;

        private var _defaultShowDelay:Number = 300;

        private var _defaultHideDelay:Number = 5000;

        private var _defaultShowEffects:IWxAbstractEffect;

        private var _defaultHideEffects:IWxAbstractEffect;

        public function WxToolTipManager(singleton:Singleton)
        {
            if (singleton == null)
            {
                throw new Error("不允许直接实例化该类型");
            }

            if (WxSystemManager.instance.stage)
            {
                onStageInit(null);
            }
            else
            {
                WxSystemManager.instance.addEventListener(WxSystemEvent.STAGE_INIT, onStageInit);
            }

            defaultShowEffect = new WxAlphaEffect(0.0, 1.0);
            defaultHideEffect = new WxAlphaEffect(1.0, 0.0);
        }

        public function get enabled():Boolean
        {
            return _enabled;
        }

        public function set enabled(value:Boolean):void
        {
            _enabled = value;
        }

        public function get defaultShowDelay():Number
        {
            return _defaultShowDelay;
        }

        public function set defaultShowDelay(value:Number):void
        {
            _defaultShowDelay = isNaN(value) ? 0 : value;
        }

        public function get defaultHideDelay():Number
        {
            return _defaultHideDelay;
        }

        public function set defaultHideDelay(value:Number):void
        {
            _defaultHideDelay = isNaN(value) ? 0 : value;
        }

        public function get defaultShowEffect():IWxAbstractEffect
        {
            return _defaultShowEffects;
        }

        public function set defaultShowEffect(value:IWxAbstractEffect):void
        {
            _defaultShowEffects = value;
        }

        public function get defaultHideEffect():IWxAbstractEffect
        {
            return _defaultHideEffects;
        }

        public function set defaultHideEffect(value:IWxAbstractEffect):void
        {
            _defaultHideEffects = value;
        }

        public function get defaultToolTipClass():Class
        {
            return _defaultToolTipClass;
        }

        public function set defaultToolTipClass(value:Class):void
        {
            // 防止默认的工具提示类被置为null
            _defaultToolTipClass = value ? value : _defaultToolTipClass;
        }

        //---------------------------------------------------
        //
        // Private Properties
        //
        //---------------------------------------------------

        private function set currentToolTip(value:IWxToolTip):void
        {
            _currentToolTip = value;
        }

        private function get currentToolTip():IWxToolTip
        {
            return _currentToolTip;
        }

        private function set currentTarget(value:DisplayObject):void
        {
            _currentTarget = value;
        }

        private function get currentTarget():DisplayObject
        {
            return _currentTarget;
        }

        private function set previousTarget(value:DisplayObject):void
        {
            _previousTarget = value;
        }

        private function get previousTarget():DisplayObject
        {
            return _previousTarget;
        }

        private function get currentTipInfo():Object
        {
            return _currentTipInfo;
        }

        private function set currentTipInfo(value:Object):void
        {
            _currentTipInfo = value;
        }

        private function get currentShowDelay():Number
        {
            return _currentShowDelay;
        }

        private function set currentShowDelay(value:Number):void
        {
            _currentShowDelay = isNaN(value) ? defaultShowDelay : value;
        }

        private function get currentHideDelay():Number
        {
            return _currentHideDelay;
        }

        private function set currentHideDelay(value:Number):void
        {
            _currentHideDelay = isNaN(value) ? defaultHideDelay : value;
        }

        private function get currentShowEffect():IWxAbstractEffect
        {
            return _currentShowEffect;
        }

        private function set currentShowEffect(value:IWxAbstractEffect):void
        {
            if (_currentShowEffect != null && _currentShowEffect != value)
            {
                _currentShowEffect.removeEventListener(WxEffectEvent.EFFECT_STOP, onShowEffectStop);
                if (_currentShowEffect.running)
                {
                    _currentShowEffect.stop();
                }
            }

            _currentShowEffect = value;

            if (_currentShowEffect != null)
            {
                _currentShowEffect.addEventListener(WxEffectEvent.EFFECT_STOP, onShowEffectStop);
            }
        }

        private function get currentHideEffect():IWxAbstractEffect
        {
            return _currentHideEffect;
        }

        private function set currentHideEffect(value:IWxAbstractEffect):void
        {
            if (_currentHideEffect != null && _currentHideEffect != value)
            {
                _currentHideEffect.removeEventListener(WxEffectEvent.EFFECT_STOP, onHideEffectStop);
                if (_currentHideEffect.running)
                {
                    _currentHideEffect.stop();
                }
            }

            _currentHideEffect = value;

            if (_currentHideEffect != null)
            {
                _currentHideEffect.addEventListener(WxEffectEvent.EFFECT_STOP, onHideEffectStop);
            }
        }

        //--------------------------------------------------
        //
        // Methods
        //
        //--------------------------------------------------

        private function initialize():void
        {
            if (!_showTimer)
            {
                _showTimer = new Timer(0, 1);
                _showTimer.addEventListener(TimerEvent.TIMER, onShowTimerComplete);
            }

            if (!_hideTimer)
            {
                _hideTimer = new Timer(0, 1);
                _hideTimer.addEventListener(TimerEvent.TIMER, onHideTimerComplete);
            }

            _initialized = true;
        }

        public function registerToolTip(tipClient:IWxToolTipManagerClient, oldTip:Object, newTip:Object):void
        {
            var target:DisplayObject = tipClient as DisplayObject;

            if (oldTip == null && newTip != null)
            {
                target.addEventListener(MouseEvent.MOUSE_MOVE, onToolTipMouseMove);
                target.addEventListener(MouseEvent.MOUSE_OUT, onToolTipMouseOut);
                if (mouseIsOver(target))
                {
                    showImmediately(target);
                }
            }
            else if (oldTip != null && newTip == null)
            {
                target.removeEventListener(MouseEvent.MOUSE_MOVE, onToolTipMouseMove);
                target.removeEventListener(MouseEvent.MOUSE_OUT, onToolTipMouseOut);
                if (mouseIsOver(target))
                {
                    hideImmediately(target);
                }
            }
        }

        public function updateToolTip(target:IWxToolTipManagerClient):void
        {
            if (target == null)
            {
                return;
            }
            if (target == currentTarget && currentToolTip != null)
            {
                currentToolTip.tipInfo = target.toolTip;
                positionTip();
            }
        }

        private function showImmediately(target:DisplayObject):void
        {
            var oldShowDelay:Number = defaultShowDelay;
            defaultShowDelay = 0;
            checkIfTargetChanged(target);
            defaultShowDelay = oldShowDelay;
        }

        private function hideImmediately(target:DisplayObject):void
        {
            checkIfTargetChanged(null);
        }

        private function mouseIsOver(target:DisplayObject):Boolean
        {
            if (target == null || target.stage == null)
            {
                return false;
            }

            if (target is WxSprite)
            {
                return WxSprite(target).mouseHitTest();
            }
            else
            {
                return target.hitTestPoint(target.stage.mouseX, target.stage.mouseY, true);
            }
        }

        private function checkIfTargetChanged(displayObject:DisplayObject):void
        {
            if (!enabled)
                return;

            findTarget(displayObject);

            if (currentTarget != previousTarget)
            {
                targetChanged();
                previousTarget = currentTarget;
            }
        }

        private function findTarget(target:DisplayObject):void
        {
            while (target)
            {
                if (target is IWxToolTipManagerClient)
                {
                    if (target is WxSprite && !((target as WxSprite).mouseHitTest()))
                    {
                        //If there are NO HIT at all, it will run the same logic as
                        //target no hit, which is the last target.
                        break;
                    }
                    currentTipInfo = IWxToolTipManagerClient(target).toolTip;
                    if (currentTipInfo != null)
                    {
                        currentTarget = target;
                        currentShowDelay = IWxToolTipManagerClient(target).toolTipShowDelay;
                        currentHideDelay = IWxToolTipManagerClient(target).toolTipHideDelay;
                        return;
                    }
                }
                target = target.parent;
            }
            currentTipInfo = null;
            currentTarget = null;
        }

        private function targetChanged():void
        {
            // Do lazy creation of the Timer objects this class uses.
            if (!_initialized)
                initialize()

            reset();

            if (currentTarget)
            {
                if (currentShowDelay == 0)
                {
                    onShowTimerComplete(null);
                }
                else
                {
                    _showTimer.delay = currentShowDelay;
                    _showTimer.start();
                }
            }
        }

        private function onShowTimerComplete(event:TimerEvent):void
        {
            if (currentTarget)
            {
                createTip();
                initializeTip();
                positionTip();
                showTip();
            }
        }

        private function onHideTimerComplete(event:TimerEvent):void
        {
            hideTip();
        }

        private function createTip():void
        {
            // 获取工具提示类
            var tipClass:Class = IWxToolTipManagerClient(currentTarget).toolTipClass;

            if (tipClass == null)
            {
                tipClass = defaultToolTipClass;
            }

            // 将来可在此处派发一个 ToolTip 创建事件
            currentToolTip = new tipClass();
        }

        private function initializeTip():void
        {
            currentToolTip.tipInfo = currentTipInfo;

            currentShowEffect = (currentToolTip.showEffect != null) ?
                currentToolTip.showEffect : defaultShowEffect;

            currentHideEffect = (currentToolTip.hideEffect != null) ?
                currentToolTip.hideEffect : defaultHideEffect;

            var i:int = 0;
            if (currentShowEffect)
            {
                currentShowEffect.target = DisplayObject(currentToolTip);
            }

            if (currentHideEffect)
            {
                currentHideEffect.target = DisplayObject(currentToolTip);
            }

        }

        private function positionTip():void
        {
            if (currentTarget == null)
            {
                return;
            }

            var pt:Point = IWxToolTipManagerClient(currentTarget).toolTipPosition;

            if (pt == null)
            {
                // 确定工具显示范围(左上角,右下角)
                var leftTop:Point = new Point(0, 0);
                var rightBottom:Point = new Point(_stageWidth, _stageHeight);
                leftTop = WxSystemManager.instance.toolTipLayer.globalToLocal(leftTop);
                rightBottom = WxSystemManager.instance.toolTipLayer.globalToLocal(rightBottom);

                // 将 工具提示 放在鼠标右下方
                var xPos:Number = WxSystemManager.instance.toolTipLayer.mouseX + 11;
                var yPos:Number = WxSystemManager.instance.toolTipLayer.mouseY + 22;

                // 防止 工具提示超过 右边界
                xPos = Math.min(xPos, rightBottom.x - currentToolTip.width);
                // 防止 工具提示超过 下边界
                yPos = Math.min(yPos, rightBottom.y - currentToolTip.height);
                // 防止 工具提示超过 左边界
                xPos = Math.max(xPos, leftTop.x);
                // 防止工具提示超过 上边界
                yPos = Math.max(yPos, leftTop.y);

                currentToolTip.x = xPos;
                currentToolTip.y = yPos;
            }
            else
            {
                pt = currentTarget.localToGlobal(pt);
                pt = WxSystemManager.instance.toolTipLayer.globalToLocal(pt);
                currentToolTip.x = pt.x;
                currentToolTip.y = pt.y;
            }
        }

        private function showTip():void
        {
            WxSystemManager.instance.toolTipLayer.addChild(currentToolTip as DisplayObject);

            // 将来可在此派发 ToolTip 显示事件
//            currentToolTip.visible = true;

            if (!currentShowEffect)
            {
                showEffectEnded();
            }
            else
            {
                currentShowEffect.start();
            }
        }

        private function hideTip():void
        {
            if (WxSystemManager.instance.toolTipLayer.contains(currentToolTip as DisplayObject))
            {
                WxSystemManager.instance.toolTipLayer.removeChild(currentToolTip as DisplayObject);
            }

            if (!currentHideEffect)
            {
                hideEffectEnded();
            }
            else
            {
                currentHideEffect.start();
            }
        }

        private function showEffectEnded():void
        {
            if (currentHideDelay == 0)
            {
                onHideTimerComplete(null);
            }
            else if (currentHideDelay != Infinity)
            {
                if (defaultHideDelay < Infinity)
                {
                    _hideTimer.delay = currentHideDelay;
                    _hideTimer.start();
                }
            }

            if (currentTarget)
            {
                // 将来可在此 派发一个 toolTip 显示特效结束事件
            }
        }

        private function hideEffectEnded():void
        {
            reset();

            if (previousTarget)
            {
                // 将来可在此 派发一个 toolTip 隐藏特效结束事件
            }

            // clear reference
            previousTarget = null;
            currentTarget = null;
            currentTipInfo = null;
        }

        private function reset():void
        {
            _showTimer.reset();
            _hideTimer.reset();

            if (previousTarget)
            {
                // 将来可在此处派发 ToolTip 隐藏事件   
            }

            currentShowEffect = null;
            currentHideEffect = null;

            if (currentToolTip)
            {
                if (WxSystemManager.instance.toolTipLayer.contains(currentToolTip as DisplayObject))
                {
                    WxSystemManager.instance.toolTipLayer.removeChild(currentToolTip as DisplayObject);
                }
                currentToolTip = null;
            }

        }

        //-----------------------------------------
        //
        // event handlers
        //
        //-----------------------------------------

        private function onToolTipMouseMove(e:MouseEvent):void
        {
            checkIfTargetChanged(DisplayObject(e.target));
        }

        private function onToolTipMouseOut(e:MouseEvent):void
        {
            checkIfTargetChanged(e.relatedObject);
        }

        private function onShowEffectStop(e:WxEffectEvent):void
        {
            showEffectEnded();
        }

        private function onHideEffectStop(e:WxEffectEvent):void
        {
            hideEffectEnded();
        }

        private function onStageResize(e:Event):void
        {
            _stageWidth = WxSystemManager.instance.stage.stageWidth;
            _stageHeight = WxSystemManager.instance.stage.stageHeight;

            positionTip();
        }

        private function onStageInit(e:WxSystemEvent):void
        {
            if (e != null)
            {
                EventDispatcher(e.currentTarget).removeEventListener(e.type, onStageInit);
            }
            WxSystemManager.instance.stage.addEventListener(Event.RESIZE, onStageResize);

            onStageResize(null);
        }
    }
}

class Singleton
{

}
